﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using System.Xml.Linq;
using System.ComponentModel;

namespace Gildor.SimpleHostMonitor.Desktop.Entities
{
    /// <summary>
    /// Stores the configuration of all hosts and the default host.
    /// </summary>
    [Serializable]
    public class HostsConfiguration : IXmlSerializable, INotifyPropertyChanged
    {
        public ObservableCollection<HostInfo> Hosts { get; private set; }

        public HostInfo Default { get; set; }

        private bool _isAlertEnabled;

        public bool IsAlertEnabled
        {
            get { return _isAlertEnabled; }
            set
            {
                _isAlertEnabled = value;
                OnPropertyChanged ("IsAlertEnabled");
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HostsConfiguration"/> class.
        /// Typically this constructor is used by serializers.
        /// </summary>
        public HostsConfiguration ()
        {
            //Create a default host in case there is none indicated 
            //in the configration file.
            Default = new HostInfo ()
            {
                FailureThreshold = 3,
                FailureThresholdSpecified = true,
                NotifyStrategy = HostInfo.NotifyStrategies.Fail,
                NotifyStrategySpecified = true
            };
            IsAlertEnabled = true;
        }

        ///// <summary>
        ///// Initializes a new instance of the <see cref="HostsConfiguration"/> class.
        ///// </summary>
        ///// <param name="hosts">The hosts.</param>
        ///// <param name="defaultHost">The default host configuration.</param>
        //public HostsConfiguration (IEnumerable<HostInfo> hosts,
        //    HostInfo defaultHost)
        //{
        //    //If the values are the same as the default host, ignore them.
        //    Hosts = new ObservableCollection<HostInfo> (hosts);

        //    Default = defaultHost;
        //}

        /// <summary>
        /// An empty <see cref="HostsConfiguration"/> instance.
        /// </summary>
        public static readonly HostsConfiguration Empty = new HostsConfiguration ()
        {
            Hosts = new ObservableCollection<HostInfo> ()
        };

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema ()
        {
            return null;
        }

        private static readonly XmlSerializer _hostsPropertySerializer =
            new XmlSerializer (typeof (ObservableCollection<HostInfo>),
                new XmlRootAttribute ("Hosts"));

        private static readonly XmlSerializer _defaultPropertySerializer =
            new XmlSerializer (typeof (HostInfo), new XmlRootAttribute ("Default"));

        private static readonly XmlSerializerNamespaces _defaultNamespace =
            new XmlSerializerNamespaces (
                new[] { new System.Xml.XmlQualifiedName (string.Empty, string.Empty) });

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The <see cref="T:System.Xml.XmlReader"/> stream from which the object is deserialized.</param>
        public void ReadXml (System.Xml.XmlReader reader)
        {
            //Deserialize data.
            reader.ReadToFollowing ("Hosts");
            using (var hostsReader = reader.ReadSubtree ())
            {
                Hosts = _hostsPropertySerializer.Deserialize (hostsReader)
                    as ObservableCollection<HostInfo>;
            }
            reader.ReadEndElement ();
            try
            {
                Default = _defaultPropertySerializer.Deserialize (reader) as HostInfo;
                IsAlertEnabled = reader
                    .ReadElementContentAsBoolean ("IsAlertEnabled", string.Empty);
            }
            catch (Exception)
            {
                //Ignore.
            }

            //Do after reading from xml.
            OnLoaded ();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The <see cref="T:System.Xml.XmlWriter"/> 
        /// stream to which the object is serialized.</param>
        public void WriteXml (System.Xml.XmlWriter writer)
        {
            OnSaving ();

            //Serialize data.
            _hostsPropertySerializer.Serialize (writer, Hosts, _defaultNamespace);
            _defaultPropertySerializer.Serialize (writer, Default, _defaultNamespace);
            //Boolean string should be converted to lowercase to be written to xml. See http://goo.gl/7rgY.
            writer.WriteElementString ("IsAlertEnabled", IsAlertEnabled.ToString ().ToLower ());
        }

        /// <summary>
        /// Called when data loaded. Do the post-loading process.
        /// </summary>
        protected virtual void OnLoaded ()
        {
            //For those properties not specified, use the value of default host.
            foreach (var host in Hosts)
            {
                if (!host.FailureThresholdSpecified)
                {
                    host.FailureThreshold = Default.FailureThreshold;
                }
                if (!host.NotifyStrategySpecified)
                {
                    host.NotifyStrategy = Default.NotifyStrategy;
                }
            }
        }

        /// <summary>
        /// Called when saving data. Do the pre-saving process.
        /// </summary>
        protected virtual void OnSaving ()
        {
            //Pre-process data.
            foreach (var host in Hosts)
            {
                host.FailureThresholdSpecified = Default == null ||
                    Default.FailureThreshold != host.FailureThreshold;
                host.NotifyStrategySpecified = Default == null ||
                    Default.NotifyStrategy != host.NotifyStrategy;
            };
        }

        #endregion

        #region INotifyPropertyChanged Members

        #region [PropertyChanged Event]

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the <see cref="E:PropertyChanged"/> event.
        /// </summary>
        /// <param name="propertyName">The property name.</param>
        protected virtual void OnPropertyChanged (string propertyName)
        {
            //TODO: More in this event-pattern.
            if (null != PropertyChanged)
            {
                PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
            }
        }

        #endregion

        #endregion
    }
}
